#if !defined(AtomHeader)
#define AtomHeader

#include <map>
#include <string.h>

#include "configuration.h"
#include "mutex.h"
#include "node.h"
#include "singleton.h"
#include "switcher.h"

// String is something inside quotation marks. Atom is not

class Atom : public Node {
public:
  Atom (const std::string &s) :
    name (s) {
  }
  virtual ~Atom () {
  }

  static Atom *create (const std::string &s);

  static const NodeType ID = 1;
  virtual NodeType type () const {
    return ID;
  }

  virtual std::string dump (int);
  virtual std::string display (int) {
    return name;
  }

  virtual bool doSwitch (Switcher &switcher) {
    return switcher.atom (*this);
  }

  std::string name;

  struct ltstr {
    bool operator () (const char *s1, const char *s2) const {
      return strcmp (s1, s2) < 0;
    }
  };
  typedef ReferenceCountedPointer<Atom> pAtom;

  class AtomPool {
  public:
    AtomPool () :
      cleanSize (200) {
    }

    void clean ();

    unsigned int cleanSize;
    Mutex mutex;

    typedef std::map<const char*, pAtom, ltstr> Atoms;
    Atoms atoms;
  };

  // Static allocation could break initialization order. Use pointers here
  static AtomPool *atomPool;
};

class String : public Atom {
public:
  String (const std::string &s) :
    Atom (s) {
  }

  static const NodeType ID = 2;
  NodeType type () const {
    return ID;
  }
  std::string dump (int);
  std::string display (int);
};

const int nDefinedNumbers = 16;

class Numbers {
public:
  Numbers ();

  std::string nil;
  pNode numbers[nDefinedNumbers];
};

class Number : public Node {
public:
  Number (const int i) :
    value (i) {
  }

  static const NodeType ID = 3;
  NodeType type () const {
    return ID;
  }
  std::string dump (int);
  std::string display (int);
  virtual bool doSwitch (Switcher &switcher) {
    return switcher.number (*this);
  }

  static Node* create (int n) {
    if(n >= 0 && n < nDefinedNumbers)
      return Singleton<Numbers> () ().numbers[n];
    else
      return new Number (n);
  }

  int value;
};

#endif
